package cn.jimmie.learn.sdk.util;

import android.content.Context;
import android.content.res.AssetManager;
import android.os.Build;

import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.List;

import dalvik.system.BaseDexClassLoader;

public class SoUtils {
    private static final String JNI = "jni";
    private static final String ARMEABI = "armeabi";
    private static final String TYPE_64 = "64";

    public static String extractSoLibs(Context context, AssetManager am) {
        String abi = getSupportAbi(am);
        if (abi == null) return null;

        String libDirPath = JNI + File.separator + abi;
        try {
            // 修正 data/data/files 目录下的JNI目录
            String filesDirPath = context.getFilesDir().getAbsolutePath();
            File jniDir = new File(filesDirPath + File.separator + libDirPath);
            if (!jniDir.exists() || !jniDir.isDirectory()) {
                //noinspection ResultOfMethodCallIgnored
                jniDir.mkdirs();
            }
            String jniDirPath = jniDir.getAbsolutePath();

            // 单个文件转移
            String[] soLibs = am.list(libDirPath);
            //noinspection ConstantConditions
            for (String soLib : soLibs) {
                String soPath = libDirPath + File.separator + soLib;
                String destPath = jniDirPath + File.separator + soLib;
                extractFile(am, soPath, destPath);
            }
            return jniDirPath;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    private static boolean isSystemLoadedBy64() {
        try {
            Object classLoader = Thread.currentThread().getContextClassLoader();
            Class<BaseDexClassLoader> BaseDexKlass = BaseDexClassLoader.class;
            //noinspection JavaReflectionMemberAccess
            Field pathListField = BaseDexKlass.getDeclaredField("pathList");
            pathListField.setAccessible(true);

            Object pathList = pathListField.get(classLoader);
            Class<?> dexPathKlass = Class.forName("dalvik.system.DexPathList");
            Field nativeLibsField = dexPathKlass.getDeclaredField("nativeLibraryDirectories");
            nativeLibsField.setAccessible(true);

            //noinspection unchecked
            List<File> nativeLibs = (List<File>) nativeLibsField.get(pathList);
            for (File nativeLib : nativeLibs) {
                System.out.println(nativeLib.getName());
                if (nativeLib.getName().contains(TYPE_64)) return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private static String[] getSystemABIS() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return Build.SUPPORTED_ABIS;
        } else {
            //noinspection deprecation
            return new String[]{
                    Build.CPU_ABI,
                    Build.CPU_ABI2
            };
        }
    }

    private static String getSupportAbi(AssetManager am) {
        String[] systemABIS = getSystemABIS();
        boolean is64 = isSystemLoadedBy64();
        try {
            //noinspection ConstantConditions
            String[] jniABIs = am.list(JNI);
            for (String abi : systemABIS) {
                if (is64 && !abi.contains(TYPE_64)) continue;

                if (isInList(jniABIs, abi)) {
                    return abi;
                }
            }
        } catch (Throwable e) {
            return null;
        }
        if (is64) {
            System.err.println("Can't load 32-bit lib, due to system load 64-bit lib");
        }
        return ARMEABI;
    }


    private static void extractFile(AssetManager am, String assetPath, String destPath) {
        InputStream is = null;
        BufferedOutputStream bos = null;
        try {
            is = am.open(assetPath);
            bos = new BufferedOutputStream(
                    new FileOutputStream(new File(destPath))
            );
            byte[] buffer = new byte[1024];
            int count;
            while ((count = is.read(buffer)) > 0) {
                bos.write(buffer, 0, count);
            }
            bos.flush();
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            closeSilently(is);
            closeSilently(bos);
        }
    }

    private static boolean isInList(String[] list, String str) {
        if (list == null || list.length == 0 || str == null) return false;
        for (String s : list) {
            if (str.equals(s)) return true;
        }
        return false;
    }

    private static void closeSilently(Closeable closeable) {
        if (closeable == null) {
            return;
        }
        try {
            closeable.close();
        } catch (Throwable ignored) {
        }
    }
}
